#include "method.h"
#include "visitordata.h"
#include "error.h"
#include "featuredeclvisitor.h"
#include "classsemantic.h"
#include <set>

std::vector<ArgEntry> linearize(EntityList * list,
								SemanticVisitorData *  data)
{
	std::vector<ArgEntry> result;
	if (!list)
		return result;
	const std::vector<EntityGroup *> & vt=list->vector();
    for (unsigned int i=0;i<vt.size();i++)
	{
		ClassTypeNode * type=vt[i]->className();
		const std::vector<IdentifierNode *> & ids=vt[i]->idList()->vector();
		for (unsigned int j=0;j<ids.size();j++)
		{
			if (contains(result,ids[j]->data()))
				data->add(new DataRedefinitionError(ids[j],NULL,"variable",ids[j]->data()));
			else
				result.push_back(ArgEntry(ids[j]->data(),type));
		}
	}
	return result;
}

void Method::setEntityLists(const std::vector<ArgEntry> & formal, 
		                    const std::vector<ArgEntry> & local
						   )
{
	m_formal_args=formal;
	m_local_vars=local;
}


void MethodDeclaration::fillAttribute(FeatureDeclarationVisitor * visitor)
{
	if (visitor->clients().size()==0)
		visitor->clients().push_back("ANY");
	else
	{
		if (visitor->clients().size()==1  && visitor->clients()[0]=="NONE")
			visitor->clients()[0]=visitor->className();
	}
    Method meth;
    meth.m_classname=visitor->className();
	meth.m_clients=visitor->clients();
	meth.m_formal_args=visitor->currentDeclaration()->formalList();
	meth.m_local_vars=visitor->currentDeclaration()->localList();
	meth.m_return_type=visitor->currentDeclaration()->returnType();
	meth.m_routine=visitor->currentDeclaration()->compound();
	meth.m_realname=visitor->currentDeclaration()->currentName();
	meth.m_type=visitor->currentDeclaration()->declType();
	meth.setNode(visitor->currentDeclaration()->currentSpec());
	if (visitor->currentDeclaration()->currentAlias())
		visitor->addOperator(visitor->currentDeclaration()->currentName(),meth,
		                     *(visitor->currentDeclaration()->currentAlias())
							);
		      
	else
		visitor->addMethod(visitor->currentDeclaration()->currentName(),meth);
}


void DeferredMethodDeclaration::fillAttribute(FeatureDeclarationVisitor * visitor)
{
	if (visitor->clients().size()==0)
		visitor->clients().push_back("ANY");
	else
	{
		if (visitor->clients().size()==1  && visitor->clients()[0]=="NONE")
			visitor->clients()[0]=visitor->className();
	}
    Method meth;
    meth.m_classname=visitor->className();
	meth.m_clients=visitor->clients();
	meth.m_formal_args=visitor->currentDeclaration()->formalList();
	meth.m_local_vars=visitor->currentDeclaration()->localList();
	meth.m_return_type=visitor->currentDeclaration()->returnType();
	meth.m_routine=visitor->currentDeclaration()->compound();
	meth.m_type=visitor->currentDeclaration()->declType();
	meth.m_realname=visitor->currentDeclaration()->currentName();
    meth.m_type=meth.m_type | FDT_ABSTRACT;
	meth.setNode(visitor->currentDeclaration()->currentSpec());
	if ((meth.m_type & FDT_FROZEN)!=0)
	{
		visitor->data()->add(new AbstractFrozen(visitor->currentDeclaration()->currentSpec(),visitor->currentDeclaration()->currentName()));
	}
	if (visitor->currentDeclaration()->currentAlias())
		visitor->addOperator(visitor->currentDeclaration()->currentName(),meth,
		                     *(visitor->currentDeclaration()->currentAlias())
							);
		      
	else
		visitor->addMethod(visitor->currentDeclaration()->currentName(),meth);
}

void FieldDeclaration::fillAttribute(FeatureDeclarationVisitor * visitor)
{
	Field f(visitor->className(),visitor->clients(),
		    visitor->currentDeclaration()->returnType(),
			visitor->currentDeclaration()->declType(),
			visitor->currentDeclaration()->currentName());
	f.setNode(visitor->currentDeclaration()->currentSpec());
	if (visitor->currentDeclaration()->currentAlias())
		visitor->data()->add(new OperatorField(f.node()));
	visitor->addField(visitor->currentDeclaration()->currentName(),f);
}

void ClassSemantic::fillAttributes(SemanticVisitorData * data)
{
  FeatureDeclarationVisitor * visitor=new FeatureDeclarationVisitor();
  visitor->setClass(m_class->name());
  visitor->setData(data);
  if (m_class->features())
  {
	  const std::vector<FeatureGroup *>  & v=m_class->features()->vector();
	  for (unsigned int i=0;i<v.size();i++)
	  {
		  v[i]->fillAttribute(visitor);
	  }
  }
  m_methods=visitor->methods();
  m_fields=visitor->fields();
  m_operators=visitor->operators();
  delete visitor;
}

MethodSignature::MethodSignature(const Method & m)
{
	returnType="";
	if (m.m_return_type)
		returnType=m.m_return_type->fullName();
	for (unsigned int i=0;i<m.m_formal_args.size();i++)
	{
		formalArgs.push_back(m.m_formal_args[i].Type->fullName());
	}
}

bool MethodSignature::operator==(const MethodSignature & o) const
{
  if (returnType!=o.returnType || formalArgs.size()!=o.formalArgs.size())
	   return false;
  for (unsigned int i=0;i<formalArgs.size();i++)
	  if (formalArgs[i]!=o.formalArgs[i])
		  return false;
  return true;
}

bool Method::hasFormalArgSignature(const std::vector<std::string> & args, const std::string & className)
{
	if (m_formal_args.size()==0)
	{
		return args.size()==0;
	}
	int currentarg=0;
	if (m_formal_args[0].Name=="CURRENT" && m_formal_args[0].Type->fullName()==m_classname)
	{
		currentarg=1;
	}
	bool ok=true;
	if (args.size()==m_formal_args.size()-currentarg) 
	{
		for (unsigned int i=currentarg;(i<m_formal_args.size()) && ok;i++)
		{
			ok= args[i-currentarg]==m_formal_args[i].Type->fullName();
		}
	}
	else 
		ok=false;
	return ok;
}

const Method * Method::getOverridenNonAbstractMethod() const
{
 for (unsigned int i=0;i<m_overriden_methods.size();i++)
 {
	 if ( (m_overriden_methods[i].methodType() & FDT_ABSTRACT )==0)
	 {
		 return &(m_overriden_methods[i]);
	 }
 }
 return NULL;
}

int Method::findFormalArg(const std::string & string)
{
	for (unsigned int i=0;i<m_formal_args.size();i++)
		if (m_formal_args[i].Name==string)
			return i;
	return -1;
}

int Method::findLocalVar(const std::string & string)
{
	for (unsigned int i=0;i<m_local_vars.size();i++)
		if (m_local_vars[i].Name==string)
			return i;
	return -1;
}

void Method::performArgCheck(ParentData * data)
{
    std::set<std::string> m_reserved;
	m_reserved.insert("CURRENT");
	m_reserved.insert("STD_OUTPUT");
	m_reserved.insert("STD_INPUT");
	m_reserved.insert("PRECURSOR");
	m_reserved.insert("RESULT");

	//Check formal arguments
	for (unsigned int i=0;i<m_formal_args.size();i++)
	{
		if (m_reserved.find(m_formal_args[i].Name)!=m_reserved.end())
		{
			data->Classes->add(new  VariableShadowing(m_formal_args[i].Type,m_formal_args[i].Name));
		}
		if (data->Classes->getClassByName(m_formal_args[i].Type)==NULL)
		{
			data->Classes->add(new TypeDoesNotExists(m_formal_args[i].Type));
		}
	}
	//Check local variables
	for (unsigned int i=0;i<m_local_vars.size();i++)
	{
		if (m_reserved.find(m_local_vars[i].Name)!=m_reserved.end())
		{
			data->Classes->add(new  VariableShadowing(m_local_vars[i].Type,m_local_vars[i].Name));
		}
		if (findFormalArg(m_local_vars[i].Name)!=-1)
		{
			data->Classes->addWarning(new  LocalVariableShadowing(m_local_vars[i].Type,m_local_vars[i].Name));
		}
		if (data->Classes->getClassByName(m_local_vars[i].Type)==NULL)
		{
			data->Classes->add(new TypeDoesNotExists(m_local_vars[i].Type));
		}
	}
	ClassTypeNode * result=returnType();
	if (result)
	{
		if (data->Classes->getClassByName(result)==NULL)
			data->Classes->add(new TypeDoesNotExists(result));
	}

}
void Method::checkFormalAndLocalVars(ParentData * data)
{
	if (ownerClass()==data->CurrentClass->cnode()->name())
		performArgCheck(data);
	

	std::string * name=new std::string(data->CurrentClass->cnode()->name());
	m_formal_args.insert(m_formal_args.begin(),ArgEntry("CURRENT",new ClassTypeNode(YYLTYPE(),name)));
	m_local_vars.insert(m_local_vars.begin(),ArgEntry("STD_OUTPUT",new ClassTypeNode(YYLTYPE(),new std::string("STD_OUTPUT_STREAM"))));
	m_local_vars.insert(m_local_vars.begin(),ArgEntry("STD_INPUT",new ClassTypeNode(YYLTYPE(),new std::string("STD_INPUT_STREAM"))));
	const Method * overmeth=getOverridenNonAbstractMethod();
	if (overmeth)
	{
		ClassTypeNode * nd=new ClassTypeNode(YYLTYPE(), new std::string(overmeth->m_class_from_which_inherited));
		m_local_vars.insert(m_local_vars.begin(),ArgEntry("PRECURSOR",nd));
	}
	if (this->returnType())
	{
		m_local_vars.push_back(ArgEntry("RESULT",this->returnType()));
	}
}

bool Method::isAvailable(const std::string & className, SemanticVisitorData * data)
{
   for (unsigned  int i=0;i<m_clients.size();i++)
   {
	   if (m_clients[i]==className)
		   return true;
	   if (data->isAncestor(m_clients[i],className))
		   return true;
   }
   return false;
}


std::string Method::ownerName()
{
 return	getJavaName(m_classname);
}

void Method::predefineSignature(const std::string & className, 
								const std::string & methodname,
								const std::string & descriptor)
{
  m_predefined_signature=true;
  m_predefined_classname=className;
  m_predefined_methname=methodname;
  m_predefined_descriptor=descriptor;
}


bool Method::hasPrecursor(Method * meth)
{
	if (m_overriden_methods.size()==0)
		return false;
	for (unsigned int i=0;i<m_overriden_methods.size();i++)
	{
		if (m_overriden_methods[i].realName()==meth->realName())
		{
			return true;
		}
	}
	return false;
}

void Method::getCommonCompilableSignature(std::string & name, std::string & descriptor)
{
	name=m_classname;
	name+=m_realname;

	descriptor="(";
	unsigned int start=0;
	if (m_formal_args.size()>0)
	{
		if (m_formal_args[0].Name=="CURRENT")
			start=1;
	}
	for (unsigned int i=start;i<m_formal_args.size();i++)
	{
		descriptor+=m_formal_args[i].Type->getSimpleSignature();
	}
	descriptor+=")";
	if (returnType())
		descriptor+=returnType()->getSimpleSignature();
	else
		descriptor+="V";
}

void Method::getFullCompilableSignature(std::string & name, std::string & descriptor)
{
	name=m_classname;
	name+=m_realname;

	descriptor="(";
	unsigned int start=0;
	if (m_formal_args.size()>0)
	{
		if (m_formal_args[0].Name=="CURRENT")
			start=1;
	}
	for (unsigned int i=start;i<m_formal_args.size();i++)
	{
		descriptor+=m_formal_args[i].Type->getFullSignature();
	}
	descriptor+=")";
	if (returnType())
		descriptor+=returnType()->getFullSignature();
	else
		descriptor+="V";
}

void Method::getRefInfo(std::string & classname,std::string & name, std::string & descriptor)
{
	if (m_predefined_signature)
	{
		classname=m_predefined_classname;
		name=m_predefined_methname;
		descriptor=m_predefined_descriptor;
	}
	else
	{
	classname=m_classname;
	getCommonCompilableSignature(name,descriptor);
	}
}

bool Method::hasSpecificArguments()
{
	if (m_formal_args.size()==0)
		return false;
	if (m_formal_args[0].Name=="CURRENT")
		return m_formal_args.size()>1;
	return true;
}